﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Ruleweaver.Rules;

namespace Ruleweaver
{
	public static class Specifiable
	{
		#region Passes / Fails

		public static Specification<T> Passes<T>(this ISpecifiable<T> specification, Expression<Func<T, bool>> nextCheck)
		{
			Contract.Requires(specification != null);
			Contract.Requires(nextCheck != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, nextCheck));
		}

		public static Specification<T> Passes<T>(this ISpecifiable<T> specification, bool nextCheckResult)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, nextCheckResult));
		}

		public static Specification<T> Fails<T>(this ISpecifiable<T> specification, Expression<Func<T, bool>> nextCheck)
		{
			Contract.Requires(specification != null);
			Contract.Requires(nextCheck != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, nextCheck));
		}

		public static Specification<T> Fails<T>(this ISpecifiable<T> specification, bool nextCheckResult)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, nextCheckResult));
		}
		#endregion

		#region Boolean

		public static Specification<bool> IsTrue(this ISpecifiable<bool> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<bool> IsFalse(this ISpecifiable<bool> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		#region Char

		public static Specification<char> IsLetterOrDigit(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsControl(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsDigit(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsHighSurrogate(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsLetter(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsLowerCase(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsLowSurrogate(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsNumber(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsPunctuation(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsSeparator(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsSurrogate(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsSymbol(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsUpperCase(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<char> IsWhiteSpace(this ISpecifiable<char> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		#region DateTime

		public static Specification<DateTime> IsWeekend(this ISpecifiable<DateTime> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<DateTime> IsWeekday(this ISpecifiable<DateTime> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<DateTime> IsLeapYear(this ISpecifiable<DateTime> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		#region Decimal

		public static Specification<decimal> IsEven(this ISpecifiable<decimal> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<decimal> IsOdd(this ISpecifiable<decimal> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<decimal> IsPositive(this ISpecifiable<decimal> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<decimal> IsNegative(this ISpecifiable<decimal> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<decimal> IsNotPositive(this ISpecifiable<decimal> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<decimal> IsNotNegative(this ISpecifiable<decimal> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<decimal> IsAdjustedPercentage(this ISpecifiable<decimal> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<decimal> IsLiteralPercentage(this ISpecifiable<decimal> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		#region Double

		public static Specification<double> IsEven(this ISpecifiable<double> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<double> IsOdd(this ISpecifiable<double> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<double> IsPositive(this ISpecifiable<double> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<double> IsNegative(this ISpecifiable<double> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<double> IsNotPositive(this ISpecifiable<double> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<double> IsNotNegative(this ISpecifiable<double> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<double> IsAdjustedPercentage(this ISpecifiable<double> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<double> IsLiteralPercentage(this ISpecifiable<double> specification)
		{
			Contract.Requires(specification != null);

			var method = (MethodInfo) MethodInfo.GetCurrentMethod();

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		#region Enumerable

		public static Specification<IEnumerable<T>> Contains<T>(this ISpecifiable<IEnumerable<T>> specification, T value)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value));
		}

		public static Specification<IEnumerable<T>> Contains<T>(
			this ISpecifiable<IEnumerable<T>> specification,
			T value,
			IEqualityComparer<T> comparer)
		{
			Contract.Requires(specification != null);
			Contract.Requires(comparer != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value, comparer));
		}

		public static Specification<IEnumerable<T>> HasNone<T>(this ISpecifiable<IEnumerable<T>> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<IEnumerable<T>> HasNone<T>(this ISpecifiable<IEnumerable<T>> specification, Expression<Func<T, bool>> itemCheck)
		{
			Contract.Requires(specification != null);
			Contract.Requires(itemCheck != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, itemCheck));
		}

		public static Specification<IEnumerable<T>> HasAny<T>(this ISpecifiable<IEnumerable<T>> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<IEnumerable<T>> HasAny<T>(this ISpecifiable<IEnumerable<T>> specification, Expression<Func<T, bool>> itemCheck)
		{
			Contract.Requires(specification != null);
			Contract.Requires(itemCheck != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, itemCheck));
		}

		public static Specification<IEnumerable<T>> HasAll<T>(this ISpecifiable<IEnumerable<T>> specification, Expression<Func<T, bool>> itemCheck)
		{
			Contract.Requires(specification != null);
			Contract.Requires(itemCheck != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, itemCheck));
		}
		#endregion

		#region In

		public static Specification<T> IsIn<T>(this ISpecifiable<T> specification, IEnumerable<T> values)
		{
			Contract.Requires(specification != null);
			Contract.Requires(values != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, values));
		}

		public static Specification<T> IsIn<T>(this ISpecifiable<T> specification, params T[] values)
		{
			Contract.Requires(specification != null);
			Contract.Requires(values != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, values));
		}

		public static Specification<T> IsIn<T>(this ISpecifiable<T> specification, IQueryable<T> values)
		{
			Contract.Requires(specification != null);
			Contract.Requires(values != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, values));
		}

		public static Specification<T> IsIn<T>(
			this ISpecifiable<T> specification,
			IEqualityComparer<T> comparer,
			IEnumerable<T> values)
		{
			Contract.Requires(specification != null);
			Contract.Requires(comparer != null);
			Contract.Requires(values != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, comparer, values));
		}

		public static Specification<T> IsIn<T>(
			this ISpecifiable<T> specification,
			IEqualityComparer<T> comparer,
			params T[] values)
		{
			Contract.Requires(specification != null);
			Contract.Requires(comparer != null);
			Contract.Requires(values != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, comparer, values));
		}

		public static Specification<T> IsIn<T>(
			this ISpecifiable<T> specification,
			IEqualityComparer<T> comparer,
			IQueryable<T> values)
		{
			Contract.Requires(specification != null);
			Contract.Requires(comparer != null);
			Contract.Requires(values != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, comparer, values));
		}
		#endregion

		#region Int32

		public static Specification<int> IsEven(this ISpecifiable<int> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<int> IsOdd(this ISpecifiable<int> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<int> IsPositive(this ISpecifiable<int> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<int> IsNegative(this ISpecifiable<int> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<int> IsNotPositive(this ISpecifiable<int> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<int> IsNotNegative(this ISpecifiable<int> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<int> IsPercentage(this ISpecifiable<int> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		#region Int64

		public static Specification<long> IsEven(this ISpecifiable<long> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<long> IsOdd(this ISpecifiable<long> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<long> IsPositive(this ISpecifiable<long> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<long> IsNegative(this ISpecifiable<long> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<long> IsNotPositive(this ISpecifiable<long> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<long> IsNotNegative(this ISpecifiable<long> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<long> IsPercentage(this ISpecifiable<long> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		#region Nullity

		public static Specification<T> IsNull<T>(this ISpecifiable<T> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<T> IsNotNull<T>(this ISpecifiable<T> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		#region Queryable

		public static Specification<IQueryable<T>> Contains<T>(this ISpecifiable<IQueryable<T>> specification, T value)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value));
		}

		public static Specification<IQueryable<T>> Contains<T>(
			this ISpecifiable<IQueryable<T>> specification,
			T value,
			IEqualityComparer<T> comparer)
		{
			Contract.Requires(specification != null);
			Contract.Requires(comparer != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value, comparer));
		}

		public static Specification<IQueryable<T>> HasNone<T>(this ISpecifiable<IQueryable<T>> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<IQueryable<T>> HasNone<T>(this ISpecifiable<IQueryable<T>> specification, Expression<Func<T, bool>> itemCheck)
		{
			Contract.Requires(specification != null);
			Contract.Requires(itemCheck != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, itemCheck));
		}

		public static Specification<IQueryable<T>> HasAny<T>(this ISpecifiable<IQueryable<T>> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<IQueryable<T>> HasAny<T>(this ISpecifiable<IQueryable<T>> specification, Expression<Func<T, bool>> itemCheck)
		{
			Contract.Requires(specification != null);
			Contract.Requires(itemCheck != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, itemCheck));
		}

		public static Specification<IQueryable<T>> HasAll<T>(this ISpecifiable<IQueryable<T>> specification, Expression<Func<T, bool>> itemCheck)
		{
			Contract.Requires(specification != null);
			Contract.Requires(itemCheck != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, itemCheck));
		}
		#endregion

		#region Relativity

		public static Specification<T> IsLessThan<T>(this ISpecifiable<T> specification, T value) where T : IComparable<T>
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value));
		}

		public static Specification<T> IsLessThanOrEqualTo<T>(this ISpecifiable<T> specification, T value) where T : IComparable<T>
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value));
		}

		public static Specification<T> IsEqualTo<T>(this ISpecifiable<T> specification, T value, IEqualityComparer<T> equalityComparer)
		{
			Contract.Requires(specification != null);
			Contract.Requires(equalityComparer != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value, equalityComparer));
		}

		public static Specification<T> IsEqualTo<T>(this ISpecifiable<T> specification, T value)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value));
		}

		public static Specification<T> IsGreaterThanOrEqualTo<T>(this ISpecifiable<T> specification, T value) where T : IComparable<T>
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value));
		}

		public static Specification<T> IsGreaterThan<T>(this ISpecifiable<T> specification, T value) where T : IComparable<T>
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, value));
		}

		public static Specification<T> IsBetween<T>(this ISpecifiable<T> specification, T minimum, T maximum, BoundaryType boundaryType)
			where T : IComparable<T>
		{
			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, minimum, maximum, boundaryType));
		}

		public static Specification<T> IsBetween<T>(this ISpecifiable<T> specification, T minimum, T maximum)
			where T : IComparable<T>
		{
			Contract.Requires(specification != null);
			Contract.Requires(minimum == null || minimum.CompareTo(maximum) <= 0);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, minimum, maximum));
		}
		#endregion

		#region Single

		public static Specification<float> IsEven(this ISpecifiable<float> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<float> IsOdd(this ISpecifiable<float> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<float> IsPositive(this ISpecifiable<float> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<float> IsNegative(this ISpecifiable<float> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<float> IsNotPositive(this ISpecifiable<float> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<float> IsNotNegative(this ISpecifiable<float> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<float> IsAdjustedPercentage(this ISpecifiable<float> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<float> IsLiteralPercentage(this ISpecifiable<float> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		#region String Content

		public static Specification<string> IsEmpty(this ISpecifiable<string> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<string> IsNotEmpty(this ISpecifiable<string> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<string> IsNotNullOrEmpty(this ISpecifiable<string> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<string> StartsWith(this ISpecifiable<string> specification, string value)
		{
			Contract.Requires(specification != null);
			Contract.Requires(!String.IsNullOrEmpty(value));

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, value));
		}

		public static Specification<string> StartsWith(this ISpecifiable<string> specification, string value, StringComparison comparisonType)
		{
			Contract.Requires(specification != null);
			Contract.Requires(!String.IsNullOrEmpty(value));

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, value, comparisonType));
		}

		public static Specification<string> StartsWith(this ISpecifiable<string> specification, string value, bool ignoreCase, CultureInfo culture)
		{
			Contract.Requires(specification != null);
			Contract.Requires(!String.IsNullOrEmpty(value));
			Contract.Requires(culture != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, value, ignoreCase, culture));
		}

		public static Specification<string> EndsWith(this ISpecifiable<string> specification, string value)
		{
			Contract.Requires(specification != null);
			Contract.Requires(!String.IsNullOrEmpty(value));

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, value));
		}

		public static Specification<string> EndsWith(this ISpecifiable<string> specification, string value, StringComparison comparisonType)
		{
			Contract.Requires(specification != null);
			Contract.Requires(!String.IsNullOrEmpty(value));

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, value, comparisonType));
		}

		public static Specification<string> EndsWith(this ISpecifiable<string> specification, string value, bool ignoreCase, CultureInfo culture)
		{
			Contract.Requires(specification != null);
			Contract.Requires(!String.IsNullOrEmpty(value));
			Contract.Requires(culture != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, value, ignoreCase, culture));
		}

		public static Specification<string> Contains(this ISpecifiable<string> specification, string value)
		{
			Contract.Requires(specification != null);
			Contract.Requires(value != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, value));
		}
		#endregion

		#region String Patterns

		public static Specification<string> Matches(this ISpecifiable<string> specification, Regex regex)
		{
			Contract.Requires(regex != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, regex));
		}

		public static Specification<string> Matches(this ISpecifiable<string> specification, string pattern)
		{
			Contract.Requires(pattern != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, pattern));
		}

		public static Specification<string> Matches(this ISpecifiable<string> specification, string pattern, RegexOptions options)
		{
			Contract.Requires(pattern != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method, pattern, options));
		}

		public static Specification<string> IsCreditCardNumber(this ISpecifiable<string> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<string> IsCreditCardCvv(this ISpecifiable<string> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<string> IsPhoneNumber(this ISpecifiable<string> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<string> IsSocialSecurityNumber(this ISpecifiable<string> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}

		public static Specification<string> IsZipCode(this ISpecifiable<string> specification)
		{
			Contract.Requires(specification != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod());

			return AppendRule(specification, Rule.Check(method));
		}
		#endregion

		public static Specification<T> IsAssignableTo<T>(this ISpecifiable<T> specification, Type type)
		{
			Contract.Requires(specification != null);
			Contract.Requires(type != null);

			var method = ((MethodInfo) MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(T));

			return AppendRule(specification, Rule.Check(method, type));
		}

		private static Specification<T> AppendRule<T>(ISpecifiable<T> baseSpecification, Rule rule)
		{
			return baseSpecification.Provider.CreateSpecification(
				baseSpecification.Target,
				Rule.And(baseSpecification.Rule, rule));
		}
	}
}